<html><body><pre class="prettyprint">
        // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

        #ifndef UNITY_IMAGE_BASED_LIGHTING_INCLUDED
        #define UNITY_IMAGE_BASED_LIGHTING_INCLUDED
        
        #include "UnityCG.cginc"
        #include "UnityStandardConfig.cginc"
        #include "UnityStandardBRDF.cginc"
        
        // ----------------------------------------------------------------------------
        
        #if 0
        
        // ----------------------------------------------------------------------------
        // Unity is Y up - left handed
        
        //-----------------------------------------------------------------------------
        // Sample generator
        //-----------------------------------------------------------------------------
        // Ref: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html
        uint <a name="ReverseBits32">ReverseBits32</a>(uint bits)
        {
        #if 0 // Shader model 5
            return reversebits(bits);
        #else
            bits = ( bits << 16) | ( bits >> 16);
            bits = ((bits & 0x00ff00ff) << 8) | ((bits & 0xff00ff00) >> 8);
            bits = ((bits & 0x0f0f0f0f) << 4) | ((bits & 0xf0f0f0f0) >> 4);
            bits = ((bits & 0x33333333) << 2) | ((bits & 0xcccccccc) >> 2);
            bits = ((bits & 0x55555555) << 1) | ((bits & 0xaaaaaaaa) >> 1);
            return bits;
        #endif
        }
        //-----------------------------------------------------------------------------
        float <a name="RadicalInverse_VdC">RadicalInverse_VdC</a>(uint bits)
        {
            return float(ReverseBits32(bits)) * 2.3283064365386963e-10; // 0x100000000
        }
        
        //-----------------------------------------------------------------------------
        float2 <a name="Hammersley2d">Hammersley2d</a>(uint i, uint maxSampleCount)
        {
            return float2(float(i) / float(maxSampleCount), RadicalInverse_VdC(i));
        }
        
        //-----------------------------------------------------------------------------
        float <a name="Hash">Hash</a>(uint s)
        {
            s = s ^ 2747636419u;
            s = s * 2654435769u;
            s = s ^ (s >> 16);
            s = s * 2654435769u;
            s = s ^ (s >> 16);
            s = s * 2654435769u;
            return float(s) / 4294967295.0f;
        }
        
        //-----------------------------------------------------------------------------
        float2 <a name="InitRandom">InitRandom</a>(float2 input)
        {
            float2 r;
            r.x = Hash(uint(input.x * 4294967295.0f));
            r.y = Hash(uint(input.y * 4294967295.0f));
        
            return r;
        }
        
        //-----------------------------------------------------------------------------
        // Util
        //-----------------------------------------------------------------------------
        
        // generate an orthonormalBasis from 3d unit vector.
        void <a name="GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)">GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)</a>
        {
            float3 upVector     = abs(N.z) < 0.999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f);
            tangentX            = normalize(cross(upVector, N));
            tangentY            = cross(N, tangentX);
        }
        
        /*
        // http://orbit.dtu.dk/files/57573287/onb_frisvad_jgt2012.pdf
        void <a name="GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)">GetLocalFrame(float3 N, out float3 tangentX, out float3 tangentY)</a>
        {
            if (N.z < -0.999f) // Handle the singularity
            {
                tangentX = Vec3f (0.0f, -1.0f, 0.0f);
                tangentY = Vec3f (-1.0f, 0.0f, 0.0f);
                return ;
            }
        
            float a     = 1.0f / (1.0f + N.z);
            float b     = -N.x * N.y * a ;
            tangentX    = float3(1.0f - N.x * N.x * a , b, -N.x);
            tangentY    = float3(b, 1.0f - N.y * N.y * a, -N.y);
        }
        */
        
        // ----------------------------------------------------------------------------
        // Sampling
        // ----------------------------------------------------------------------------
        
        void <a name="ImportanceSampleCosDir">ImportanceSampleCosDir</a>(float2 u,
                                    float3 N,
                                    float3 tangentX,
                                    float3 tangentY,
                                    out float3 L)
        {
            // Cosine sampling - ref: http://www.rorydriscoll.com/2009/01/07/better-sampling/
            float cosTheta = sqrt(max(0.0f, 1.0f - u.x));
            float sinTheta = sqrt(u.x);
            float phi = UNITY_TWO_PI * u.y;
        
            // Transform from spherical into cartesian
            L = float3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
            // Local to world
            L = tangentX * L.x + tangentY * L.y + N * L.z;
        }
        
        //-------------------------------------------------------------------------------------
        void <a name="ImportanceSampleGGXDir">ImportanceSampleGGXDir</a>(float2 u,
                                    float3 V,
                                    float3 N,
                                    float3 tangentX,
                                    float3 tangentY,
                                    float roughness,
                                    out float3 H,
                                    out float3 L)
        {
            // GGX NDF sampling
            float cosThetaH = sqrt((1.0f - u.x) / (1.0f + (roughness * roughness - 1.0f) * u.x));
            float sinThetaH = sqrt(max(0.0f, 1.0f - cosThetaH * cosThetaH));
            float phiH      = UNITY_TWO_PI * u.y;
        
            // Transform from spherical into cartesian
            H = float3(sinThetaH * cos(phiH), sinThetaH * sin(phiH), cosThetaH);
            // Local to world
            H = tangentX * H.x + tangentY * H.y + N * H.z;
        
            // Convert sample from half angle to incident angle
            L = 2.0f * dot(V, H) * H - V;
        }
        
        // ----------------------------------------------------------------------------
        // weightOverPdf return the weight (without the diffuseAlbedo term) over pdf. diffuseAlbedo term must be apply by the caller.
        void <a name="ImportanceSampleLambert">ImportanceSampleLambert</a>(
            float2 u,
            float3 N,
            float3 tangentX,
            float3 tangentY,
            out float3 L,
            out float NdotL,
            out float weightOverPdf)
        {
            ImportanceSampleCosDir(u, N, tangentX, tangentY, L);
        
            NdotL = saturate(dot(N, L));
        
            // Importance sampling weight for each sample
            // pdf = N.L / PI
            // weight = fr * (N.L) with fr = diffuseAlbedo / PI
            // weight over pdf is:
            // weightOverPdf = (diffuseAlbedo / PI) * (N.L) / (N.L / PI)
            // weightOverPdf = diffuseAlbedo
            // diffuseAlbedo is apply outside the function
        
            weightOverPdf = 1.0f;
        }
        
        // ----------------------------------------------------------------------------
        // weightOverPdf return the weight (without the Fresnel term) over pdf. Fresnel term must be apply by the caller.
        void <a name="ImportanceSampleGGX">ImportanceSampleGGX</a>(
            float2 u,
            float3 V,
            float3 N,
            float3 tangentX,
            float3 tangentY,
            float roughness,
            float NdotV,
            out float3 L,
            out float VdotH,
            out float NdotL,
            out float weightOverPdf)
        {
            float3 H;
            ImportanceSampleGGXDir(u, V, N, tangentX, tangentY, roughness, H, L);
        
            float NdotH = saturate(dot(N, H));
            // Note: since L and V are symmetric around H, LdotH == VdotH
            VdotH = saturate(dot(V, H));
            NdotL = saturate(dot(N, L));
        
            // Importance sampling weight for each sample
            // pdf = D(H) * (N.H) / (4 * (L.H))
            // weight = fr * (N.L) with fr = F(H) * G(V, L) * D(H) / (4 * (N.L) * (N.V))
            // weight over pdf is:
            // weightOverPdf = F(H) * G(V, L) * (L.H) / ((N.H) * (N.V))
            // weightOverPdf = F(H) * 4 * (N.L) * V(V, L) * (L.H) / (N.H) with V(V, L) = G(V, L) / (4 * (N.L) * (N.V))
            // F is apply outside the function
        
            float Vis = SmithJointGGXVisibilityTerm(NdotL, NdotV, roughness);
            weightOverPdf = 4.0f * Vis * NdotL * VdotH / NdotH;
        }
        
        //-----------------------------------------------------------------------------
        // Reference
        // ----------------------------------------------------------------------------
        
        // Ref: Moving Frostbite to PBR (Appendix A)
        void <a name="IntegrateLambertDiffuseIBLRef">IntegrateLambertDiffuseIBLRef</a>( out float3 diffuseLighting,
                                            UNITY_ARGS_TEXCUBE(tex),
                                            float4 texHdrParam, // Multiplier to apply on hdr texture (in case of rgbm)
                                            float3 N,
                                            float3 diffuseAlbedo,
                                            uint sampleCount = 2048)
        {
            float3 acc      = float3(0.0f, 0.0f, 0.0f);
            // Add some jittering on Hammersley2d
            float2 randNum  = InitRandom(N.xy * 0.5f + 0.5f);
        
            float3 tangentX, tangentY;
            GetLocalFrame(N, tangentX, tangentY);
        
            for (uint i = 0; i < sampleCount; ++i)
            {
                float2 u    = Hammersley2d(i, sampleCount);
                u           = frac(u + randNum + 0.5f);
        
                float3 L;
                float NdotL;
                float weightOverPdf;
                ImportanceSampleLambert(u, N, tangentX, tangentY, L, NdotL, weightOverPdf);
        
                if (NdotL > 0.0f)
                {
                    float4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, L, 0).rgba;
                    float3 val = DecodeHDR(rgbm, texHdrParam);
        
                    // diffuse Albedo is apply here as describe in ImportanceSampleLambert function
                    acc += diffuseAlbedo * weightOverPdf * val;
                }
            }
        
            diffuseLighting = acc / sampleCount;
        }
        
        // ----------------------------------------------------------------------------
        
        void <a name="IntegrateDisneyDiffuseIBLRef">IntegrateDisneyDiffuseIBLRef</a>(  out float3 diffuseLighting,
                                            UNITY_ARGS_TEXCUBE(tex),
                                            float4 texHdrParam, // Multiplier to apply on hdr texture (in case of rgbm)
                                            float3 N,
                                            float3 V,
                                            float roughness,
                                            float3 diffuseAlbedo,
                                            uint sampleCount = 2048)
        {
            float NdotV = dot(N, V);
            float3 acc  = float3(0.0f, 0.0f, 0.0f);
            // Add some jittering on Hammersley2d
            float2 randNum  = InitRandom(N.xy * 0.5f + 0.5f);
        
            float3 tangentX, tangentY;
            GetLocalFrame(N, tangentX, tangentY);
        
            for (uint i = 0; i < sampleCount; ++i)
            {
                float2 u    = Hammersley2d(i, sampleCount);
                u           = frac(u + randNum + 0.5f);
        
                float3 L;
                float NdotL;
                float weightOverPdf;
                // for Disney we still use a Cosine importance sampling, true Disney importance sampling imply a look up table
                ImportanceSampleLambert(u, N, tangentX, tangentY, L, NdotL, weightOverPdf);
        
                if (NdotL > 0.0f)
                {
                    float4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, L, 0).rgba;
                    float3 val = DecodeHDR(rgbm, texHdrParam);
        
                    float3 H = normalize(L + V);
                    float LdotH = dot(L, H);
                    // Note: we call DisneyDiffuse that require to multiply by Albedo / PI. Divide by PI is already taken into account
                    // in weightOverPdf of ImportanceSampleLambert call.
                    float disneyDiffuse = DisneyDiffuse(NdotV, NdotL, LdotH, RoughnessToPerceptualRoughness(roughness));
        
                    // diffuse Albedo is apply here as describe in ImportanceSampleLambert function
                    acc += diffuseAlbedo * disneyDiffuse * weightOverPdf * val;
                }
            }
        
            diffuseLighting = acc / sampleCount;
        }
        
        // ----------------------------------------------------------------------------
        // Ref: Moving Frostbite to PBR (Appendix A)
        void <a name="IntegrateSpecularGGXIBLRef">IntegrateSpecularGGXIBLRef</a>(out float3 specularLighting,
                                        UNITY_ARGS_TEXCUBE(tex),
                                        float4 texHdrParam, // Multiplier to apply on hdr texture (in case of rgbm)
                                        float3 N,
                                        float3 V,
                                        float roughness,
                                        float3 f0,
                                        float f90,
                                        uint sampleCount = 2048)
        {
            float NdotV     = saturate(dot(N, V));
            float3 acc      = float3(0.0f, 0.0f, 0.0f);
            // Add some jittering on Hammersley2d
            float2 randNum  = InitRandom(V.xy * 0.5f + 0.5f);
        
            float3 tangentX, tangentY;
            GetLocalFrame(N, tangentX, tangentY);
        
            for (uint i = 0; i < sampleCount; ++i)
            {
                float2 u    = Hammersley2d(i, sampleCount);
                u           = frac(u + randNum + 0.5f);
        
                float VdotH;
                float NdotL;
                float3 L;
                float weightOverPdf;
        
                // GGX BRDF
                ImportanceSampleGGX(u, V, N, tangentX, tangentY, roughness, NdotV,
                                    L, VdotH, NdotL, weightOverPdf);
        
                if (NdotL > 0.0f)
                {
                    // Fresnel component is apply here as describe in ImportanceSampleGGX function
                    float3 FweightOverPdf = FresnelLerp(f0, f90, VdotH) * weightOverPdf;
        
                    float4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, L, 0).rgba;
                    float3 val = DecodeHDR(rgbm, texHdrParam);
        
                    acc += FweightOverPdf * val;
                }
            }
        
            specularLighting = acc / sampleCount;
        }
        
        // ----------------------------------------------------------------------------
        // Pre-integration
        // ----------------------------------------------------------------------------
        
        // Ref: Listing 18 in "Moving Frostbite to PBR" + https://knarkowicz.wordpress.com/2014/12/27/analytical-dfg-term-for-ibl/
        float4 <a name="IntegrateDFG">IntegrateDFG</a>(float3 V, float3 N, float roughness, uint sampleCount)
        {
            float NdotV     = saturate(dot(N, V));
            float4 acc      = float4(0.0f, 0.0f, 0.0f, 0.0f);
            // Add some jittering on Hammersley2d
            float2 randNum  = InitRandom(V.xy * 0.5f + 0.5f);
        
            float3 tangentX, tangentY;
            GetLocalFrame(N, tangentX, tangentY);
        
            for (uint i = 0; i < sampleCount; ++i)
            {
                float2 u    = Hammersley2d(i, sampleCount);
                u           = frac(u + randNum + 0.5f);
        
                float VdotH;
                float NdotL;
                float weightOverPdf;
        
                float3 L; // Unused
                ImportanceSampleGGX(u, V, N, tangentX, tangentY, roughness, NdotV,
                                    L, VdotH, NdotL, weightOverPdf);
        
                if (NdotL > 0.0f)
                {
                    // Integral is
                    //   1 / NumSample * \int[  L * fr * (N.L) / pdf ]  with pdf =  D(H) * (N.H) / (4 * (L.H)) and fr = F(H) * G(V, L) * D(H) / (4 * (N.L) * (N.V))
                    // This is split  in two part:
                    //   A) \int[ L * (N.L) ]
                    //   B) \int[ F(H) * 4 * (N.L) * V(V, L) * (L.H) / (N.H) ] with V(V, L) = G(V, L) / (4 * (N.L) * (N.V))
                    //      = \int[ F(H) * weightOverPdf ]
        
                    // Recombine at runtime with: ( f0 * weightOverPdf * (1 - Fc) + f90 * weightOverPdf * Fc ) with Fc =(1 - V.H)^5
                    float Fc            = pow(1.0f - VdotH, 5.0f);
                    acc.x               += (1.0f - Fc) * weightOverPdf;
                    acc.y               += Fc * weightOverPdf;
                }
        
                // for Disney we still use a Cosine importance sampling, true Disney importance sampling imply a look up table
                ImportanceSampleLambert(u, N, tangentX, tangentY, L, NdotL, weightOverPdf);
        
                if (NdotL > 0.0f)
                {
                    float3 H = normalize(L + V);
                    float LdotH = dot(L, H);
                    float disneyDiffuse = DisneyDiffuse(NdotV, NdotL, LdotH, RoughnessToPerceptualRoughness(roughness));
        
                    acc.z += disneyDiffuse * weightOverPdf;
                }
            }
        
            return acc / sampleCount;
        }
        
        // ----------------------------------------------------------------------------
        // Ref: Listing 19 in "Moving Frostbite to PBR"
        // IntegrateLD will not work with RGBM cubemap. For now it is use with fp16 cubemap such as those use for real time cubemap.
        float4 <a name="IntegrateLD">IntegrateLD</a>( UNITY_ARGS_TEXCUBE(tex),
                            float3 V,
                            float3 N,
                            float roughness,
                            float mipmapcount,
                            float invOmegaP,
                            uint sampleCount,
                            bool prefilter = true) // static bool
        {
            float3 acc          = float3(0.0f, 0.0f, 0.0f);
            float  accWeight    = 0;
        
            float2 randNum  = InitRandom(V.xy * 0.5f + 0.5f);
        
            float3 tangentX, tangentY;
            GetLocalFrame(N, tangentX, tangentY);
        
            for (uint i = 0; i < sampleCount; ++i)
            {
                float2 u    = Hammersley2d(i, sampleCount);
                u           = frac(u + randNum + 0.5f);
        
                float3 H;
                float3 L;
                ImportanceSampleGGXDir(u, V, N, tangentX, tangentY, roughness, H, L);
        
                float NdotL = saturate(dot(N,L));
        
                float mipLevel;
        
                if (!prefilter) // BRDF importance sampling
                {
                    mipLevel = 0.0f;
                }
                else // Prefiltered BRDF importance sampling
                {
                    float NdotH = saturate(dot(N, H));
                    // Note: since L and V are symmetric around H, LdotH == VdotH
                    float LdotH = saturate(dot(L, H));
        
                    // Use pre - filtered importance sampling (i.e use lower mipmap
                    // level for fetching sample with low probability in order
                    // to reduce the variance ).
                    // ( Reference : GPU Gem3: http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html)
                    //
                    // Since we pre - integrate the result for normal direction ,
                    // N == V and then NdotH == LdotH . This is why the BRDF pdf
                    // can be simplifed from :
                    // pdf = D * NdotH /(4* LdotH ) to pdf = D / 4;
                    //
                    // - OmegaS : Solid angle associated to a sample
                    // - OmegaP : Solid angle associated to a pixel of the cubemap
        
                    float pdf       = GGXTerm(NdotH, roughness) * NdotH / (4 * LdotH);
                    float omegaS    = 1.0f / (sampleCount * pdf);                           // Solid angle associated to a sample
                    // invOmegaP is precomputed on CPU and provide as a parameter of the function
                    // float omegaP = UNITY_FOUR_PI / (6.0f * cubemapWidth * cubemapWidth); // Solid angle associated to a pixel of the cubemap
                    // Clamp is not necessary as the hardware will do it.
                    // mipLevel     = clamp(0.5f * log2(omegaS * invOmegaP), 0, mipmapcount);
                    mipLevel        = 0.5f * log2(omegaS * invOmegaP); // Clamp is not necessary as the hardware will do it.
                }
        
                if (NdotL > 0.0f)
                {
                    // No rgbm format here, only fp16
                    float3 val = UNITY_SAMPLE_TEXCUBE_LOD(tex, L, mipLevel).rgba;
        
                    // See p63 equation (53) of moving Frostbite to PBR v2 for the extra NdotL here (both in weight and value)
                    acc             += val * NdotL;
                    accWeight       += NdotL;
                }
            }
        
            return float4(acc * (1.0f / accWeight), 1.0f);
        }
        
        #endif // 0
        
        // ----------------------------------------------------------------------------
        // GlossyEnvironment - Function to integrate the specular lighting with default sky or reflection probes
        // ----------------------------------------------------------------------------
        struct <a name="Unity_GlossyEnvironmentData">Unity_GlossyEnvironmentData</a>
        {
            // - Deferred case have one cubemap
            // - Forward case can have two blended cubemap (unusual should be deprecated).
        
            // Surface properties use for cubemap integration
            half    <a name="Unity_GlossyEnvironmentData.roughness">roughness</a>; // CAUTION: This is perceptualRoughness but because of compatibility this name can't be change :(
            half3   <a name="Unity_GlossyEnvironmentData.reflUVW">reflUVW</a>;
        };
        
        // ----------------------------------------------------------------------------
        
        Unity_GlossyEnvironmentData <a name="UnityGlossyEnvironmentSetup">UnityGlossyEnvironmentSetup</a>(half Smoothness, half3 worldViewDir, half3 Normal, half3 fresnel0)
        {
            Unity_GlossyEnvironmentData g;
        
            g.roughness /* perceptualRoughness */   = SmoothnessToPerceptualRoughness(Smoothness);
            g.reflUVW   = reflect(-worldViewDir, Normal);
        
            return g;
        }
        
        // ----------------------------------------------------------------------------
        half <a name="perceptualRoughnessToMipmapLevel">perceptualRoughnessToMipmapLevel</a>(half perceptualRoughness)
        {
            return perceptualRoughness * UNITY_SPECCUBE_LOD_STEPS;
        }
        
        // ----------------------------------------------------------------------------
        half <a name="mipmapLevelToPerceptualRoughness">mipmapLevelToPerceptualRoughness</a>(half mipmapLevel)
        {
            return mipmapLevel / UNITY_SPECCUBE_LOD_STEPS;
        }
        
        // ----------------------------------------------------------------------------
        half3 <a name="Unity_GlossyEnvironment">Unity_GlossyEnvironment</a> (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn)
        {
            half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;
        
        // TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
        // For now disabled
        #if 0
            float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
            const float fEps = 1.192092896e-07F;        // smallest such that 1.0+FLT_EPSILON != 1.0  (+1e-4h is NOT good here. is visibly very wrong)
            float n =  (2.0/max(fEps, m*m))-2.0;        // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf
        
            n /= 4;                                     // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html
        
            perceptualRoughness = pow( 2/(n+2), 0.25);      // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
        #else
            // MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.
            perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
        #endif
        
        
            half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);
            half3 R = glossIn.reflUVW;
            half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);
        
            return DecodeHDR(rgbm, hdr);
        }
        
        // ----------------------------------------------------------------------------
        // Include deprecated function
        #define INCLUDE_UNITY_IMAGE_BASED_LIGHTING_DEPRECATED
        #include "UnityDeprecated.cginc"
        #undef INCLUDE_UNITY_IMAGE_BASED_LIGHTING_DEPRECATED
        
        // ----------------------------------------------------------------------------
        
        #endif // UNITY_IMAGE_BASED_LIGHTING_INCLUDED
</pre>
<script src="https://xibanya.github.io/ShaderTutorials/CGIncludes/code-prettify/loader/run_prettify.js?skin=vscode"></script>
<script src="https://xibanya.github.io/ShaderTutorials/CGIncludes/Data/link_definitions.js"></script>
</body></html>